using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace Skinner
{
    public class StyleSheet
    {
        private System.Collections.Generic.SortedDictionary<string, ColorRule> _colors = new System.Collections.Generic.SortedDictionary<string, ColorRule>();
        private System.Collections.Generic.Dictionary<string, StyleRule> _rules = new Dictionary<string, StyleRule>();
        private System.Collections.Generic.SortedDictionary<string, string> _backgroundImages = new SortedDictionary<string, string>();
        private System.Collections.Generic.Dictionary<string, string> _replacementImages = new Dictionary<string, string>();

        public System.Collections.Generic.Dictionary<string, StyleRule> Rules
        {
            get { return _rules; }
        }

        public System.Collections.Generic.SortedDictionary<string, ColorRule> Colors
        {
            get { return _colors; }
        }

        public System.Collections.Generic.SortedDictionary<string, string> BackgroundImages
        {
            get { return _backgroundImages; }
        }

        public System.Collections.Generic.Dictionary<string, string> ReplacementImages
        {
            get { return _replacementImages; }
        }

        private string _href;

        public string HREF
        {
            get { return _href; }
            set { _href = value; }
        }

        public int RuleCount
        {
            get { return _rules.Count; }
        }

        private mshtml.IHTMLStyleSheet _sheet = null;
        public mshtml.IHTMLStyleSheet Sheet
        {
            get { return _sheet; }
        }

        internal StyleSheet(mshtml.IHTMLStyleSheet sheet)
        {
            _sheet = sheet;
            _href = (_sheet.href != null && _sheet.href != string.Empty? _sheet.href : "Embedded");

            StyleRule rule = null;
            for(int i=0; i<_sheet.rules.length; i++)
            {
                rule = new StyleRule(_sheet.rules.item(i), this);
                
                if (!_rules.ContainsKey(rule.Rule.selectorText.ToUpper()))
                {
                    _rules.Add(rule.Rule.selectorText.ToUpper(), rule);
                }
                else
                {
                    _rules[rule.Rule.selectorText.ToUpper()].Rules.Add(rule.Rule);
                }
                try
                {
                    if (rule.Rule.style.backgroundColor != null)
                        AddColor(rule.Rule.style.backgroundColor.ToString(), rule.Rule.selectorText);
                    if (rule.Rule.style.borderBottomColor != null)
                        AddColor(rule.Rule.style.borderBottomColor.ToString(), rule.Rule.selectorText);
                    if (rule.Rule.style.borderColor != null)
                        AddColor(rule.Rule.style.borderColor.ToString(), rule.Rule.selectorText);
                    if (rule.Rule.style.borderLeftColor != null)
                        AddColor(rule.Rule.style.borderLeftColor.ToString(), rule.Rule.selectorText);
                    if (rule.Rule.style.borderRightColor != null)
                        AddColor(rule.Rule.style.borderRightColor.ToString(), rule.Rule.selectorText);
                    if (rule.Rule.style.borderTopColor != null)
                        AddColor(rule.Rule.style.borderTopColor.ToString(), rule.Rule.selectorText);
                    if (rule.Rule.style.color != null)
                        AddColor(rule.Rule.style.color.ToString(), rule.Rule.selectorText);
                    if (rule.Rule.style.backgroundImage != null)
                    {
                        if (!_backgroundImages.ContainsKey(rule.Rule.selectorText.ToUpper()))
                        {
                            _backgroundImages.Add(rule.Rule.selectorText.ToUpper(), rule.Rule.style.backgroundImage);
                        }
                        else
                        {
                            _backgroundImages[rule.Rule.selectorText.ToUpper()] = rule.Rule.style.backgroundImage;
                        }
                        
                        //When loading a file there may be replacement images.
                        if (FixBackgroundImage(rule.Rule.style.backgroundImage).Substring(1, 2) == @":\")
                        {
                            _replacementImages.Add(rule.Rule.selectorText.ToUpper(), FixBackgroundImage(rule.Rule.style.backgroundImage));
                        }
                    }
                }
                catch { }
            }
        }

        //LAME! Refactor this into a static member somewhere so there aren't two copies!
        private string FixBackgroundImage(string backgroundImage)
        {
            if (backgroundImage.ToLower().Substring(0, 4) == "url(")
            {
                backgroundImage = backgroundImage.Substring(4);
            }

            backgroundImage = backgroundImage.Replace(")", "");

            return backgroundImage;
        }

        
        private void AddColor(string color, string selectorText)
        {
            if (!_colors.ContainsKey(color))
            {
                _colors.Add(color, new ColorRule(color, selectorText));
            }
            else
            {
                ColorRule rule = _colors[color];
                if(!rule.RuleSelectors.Contains(selectorText))
                    rule.RuleSelectors.Add(selectorText);
            }
        }

        //TODO: Refactor this and the one below
        public static List<string> GetMatchingStyles(HtmlDocument page, StyleSheet sheet)
        {
            List<string> matchedStyles = new List<string>();
            HtmlElementCollection elements = page.All;

            //Get all of the matching elements.
            foreach (HtmlElement element in elements)
            {
                mshtml.IHTMLElement domElement = (mshtml.IHTMLElement)element.DomElement;
                if (domElement.className != null && domElement.className != string.Empty)
                {
                    List<string> classes = DecomposeClass(domElement.className, element.TagName, false);

                    bool found = false;
                    foreach (string className in classes)
                    {
                        if (sheet.Rules.ContainsKey(className.ToUpper()))
                        {
                            found = true;

                            //Add the element to the sheet's element
                            if (!sheet.Rules[className.ToUpper()].MatchingElements.Contains(element))
                            {
                                sheet.Rules[className.ToUpper()].MatchingElements.Add(element);
                            }

                            //Add the class to the match list
                            if (!matchedStyles.Contains(className))
                            {
                                matchedStyles.Add(className);
                            }
                        }
                    }
                    if (!found)
                    {    //didn't get a hit on the key which is possible with things like Table A, DIV A, TABLE TD. So, do slow search
                        found = false;

                        classes = DecomposeClass(domElement.className, element.TagName, true);
                        foreach (string className in classes)
                        {
                            if (className.Length > 1)
                            {
                                foreach (StyleRule rule in sheet.Rules.Values)
                                {
                                    if (rule.Rule.selectorText.ToUpper().Contains(className.ToUpper()))
                                    {
                                        found = true;

                                        //Add the element to the sheet's element
                                        if (!rule.MatchingElements.Contains(element))
                                        {
                                            rule.MatchingElements.Add(element);
                                        }

                                        //Add the class to the match list
                                        if (!matchedStyles.Contains(rule.Rule.selectorText))
                                        {
                                            matchedStyles.Add(rule.Rule.selectorText);
                                        }

                                        break;
                                    }
                                }
                            }
                        }

                        if (!found)
                            System.Diagnostics.Debug.Print(domElement.className);
                    }
                }
            }

            return matchedStyles;
        }

        //TODO: Refactor this and the one above
        public static string GetMatchingStyle(string className, HtmlElement element, StyleSheet sheet)
        {
            string matchedStyle = string.Empty;
            mshtml.IHTMLElement domElement = (mshtml.IHTMLElement)element.DomElement;
            
            List<string> classes = DecomposeClass(className, element.TagName, false);

            bool found = false;
            foreach (string key in classes)
            {
                if (sheet.Rules.ContainsKey(key.ToUpper()))
                {
                    found = true;
                    matchedStyle = key;
                }
            }
            if (!found)
            {    //didn't get a hit on the key which is possible with things like Table A, DIV A, TABLE TD. So, do slow search
                found = false;

                classes = DecomposeClass(className, element.TagName, true);
                foreach (string key in classes)
                {
                    if (key.Length > 1)
                    {
                        foreach (StyleRule rule in sheet.Rules.Values)
                        {
                            if (rule.Rule.selectorText.ToUpper().Contains(key.ToUpper()))
                            {
                                found = true;
                                matchedStyle = rule.Rule.selectorText;
                                break;
                            }
                        }
                    }
                }

                if (!found)
                    System.Diagnostics.Debug.Print(domElement.className);
            }

            return matchedStyle;
        }

        //Force element type means include the tagname.rule for the longer search when we are doing contains.
        static List<string> DecomposeClass(string className, string elementType, bool forceElementType)
        {
            char[] splitter = { ' ' };
            string[] splitClass = className.Split(splitter, StringSplitOptions.None);
            List<string> classes = new List<string>();

            for (int i = 0; i < splitClass.Length; i++)
            {
                if (!splitClass[i].StartsWith("."))
                    splitClass[i] = "." + splitClass[i];

                if (!forceElementType)
                    classes.Add(splitClass[i]);

                classes.Add(elementType + splitClass[i]);
            }

            return classes;
        }
    }
}
